package br.ufpa.cbcc2007.dikemon.map;

import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;

import br.ufpa.cbcc2007.dikemon.engine.Engine;

public class Actor extends Sprite implements Actualizeable, Collideable{
	private int faceDir; // down; right, up, left;
	private boolean walking, animMotion, animStop; // São verdadeiros se, respectivamente, está andando, 
												   //se mostra animação quando se move, e se mostra quando parado.
	private boolean stopWithMapColision;		   // Se cancela o movento ao colidir com de tile de parede
	private byte waitStep; 		// O número de frames antes de completar um passo. 
	private int pastFrames=0;	// Quando está em movimento, é contado quantos frames passaram desde o ínicio deste movimento
	private int walkFrame=1;	// Em qual frame de animação está, pode ser apenas 1 ou 2.
	private int incX;			// O número de pixels que o ator se movimentará no eixo X enquando anda.
	private int incY;			// O número de pixels que o ator se movimentará no eixo Y enquando anda.
	private int gridX=-1;		// A posição atual na grade (coluna)
	private int gridY=-1;		// A posição atual na grade (linha)
	private int prevGridX;		// A posição anterior na grade (coluna)
	private int prevGridY;		// A posição anterior na grade (linha)

	//Consturtor
	public Actor(Image image, int frameWidth, int frameHeight, int gridX2, int gridY2) {
		super(image, frameWidth, frameHeight);
		faceDir = 0;
		walking = false;
		animMotion = true;
		animStop = false;
		waitStep = 4;
		gridX = gridX2;
		gridY = gridY2;
		stopWithMapColision = true;
	}

	//Atualização: Todo ator é atualizavel por isso implemta este método que é chamado em cada 
	// frame durante a atualização lógica.
	public void actualize() {
		nextFrame();  //Simplesmente passa para o próximo quadro de animação
		if(walking){  // Se estiver andando
			pastFrames++;	//Incrementar o número de frames (indicando que mais um passou)
			setPosition(getX()+incX, getY()+incY);  // Atualizar a posição e pixels 
											 	    //(a da grade é feita logo na ordem de movimento.
			if(pastFrames==waitStep){				// Quando terminar o passo o ator deve
				walking = false;					//  Parar de andar
				align2Grig();						//  E alinhar-se com a grade.
			}
		}
		else if (animStop) pastFrames++;			// Se estiver animado parado, também deve haver contagem de frames.
	}
	
	//Para que uma única imagem represente todos os estados do actor, é preciso mudar a rotina que faz a 
	// substituição do frame para a animação.
	// Em vez da sequencia 0-1-2-3...N-0...
	// usa-se a apenas o frame DIR(0, 3, 6 ou 9) se estiver parado ou
	// (DIR+1)-(DIR+2)-(DIR+1)-(DIR+2)... se estiver animado.
	public void nextFrame() {
		if((!walking&&!animStop) || (walking&&!animMotion)){
			setFrame(faceDir*3);
		}
		else {
			if(pastFrames%8==0) walkFrame = (walkFrame==1)?2:1;
			setFrame(faceDir*3+walkFrame);
		}
	}
	
	//Método para andar em uma direção. Começando por 0, temos baixo, direita, cima e esquerda;
	public void walk(int dir){
		if(walking) return;					//Se estiver andando, ignore esta ordem (termine o movimento que já está em curso
		walking = true;						//Senão comece a andar
		pastFrames = 0;						//Resete o contador de frames
		faceDir = dir;						//Vire-se para a direção que vai andar
		int add = Engine.TILESIZE/waitStep; //Aqui calcula-se qual vai ser o incremento em pixels,com base no tamanho 
											// de um tile e número de frames necessários para executar um passo
		switch (dir) {						//Aqui verifica-se qual eixo será incrementado e em que sentido.
		case 0:
			incY = add;
			incX = 0; 
			setGridPosition(gridX, gridY+1);
			break;
		case 1:
			incX = add;
			incY = 0; 
			setGridPosition(gridX+1, gridY);
			break;
		case 2:
			incY = -add;
			incX = 0; 
			setGridPosition(gridX, gridY-1);
			break;
		case 3:
			incX = -add;
			incY = 0;
			setGridPosition(gridX-1, gridY); 
			break;
		default:
			break;
		}
	}

	//Quando colidir com outro Sprite, a ação básica é retornar a posição anterior e se alinhar novamente
	public void collides(Collideable other) {
		restoreGridPosition();
		align2Grig();
	}
	
	//Quando colidir com um tile (provavelmente da segunda camada), a ação básica é retornar a posição 
	// anterior e se alinhar novamente. Porem, se a flag stopWithMapColision estiver desmaracada, ele
	// poderá atravessar paredes.
	public void collidesTile() {
		if(stopWithMapColision){
			restoreGridPosition();
			align2Grig();
		}
	}

	public void setAnimStop(boolean animStop) {
		this.animStop = animStop;
	}

	public boolean isAnimStop() {
		return animStop;
	}

	public void setAnimMotion(boolean animMotion) {
		this.animMotion = animMotion;
	}

	public boolean isAnimMotion() {
		return animMotion;
	}

	public void setFaceDir(int i) {
		faceDir = i;
	}
	
	public int getFaceDir() {
		return faceDir;
	}

	public void setVelocity(byte velocity) {
		this.waitStep = (byte) (8-velocity);
	}

	public byte getVelocity() {
		return (byte) (8-waitStep);
	}


	public int getGridX() {
		return gridX;
	}

	public void setGridPosition(int gridX, int gridY) {
		this.prevGridX = this.gridX;
		this.prevGridY = this.gridY;
		this.gridX = gridX;
		this.gridY = gridY;
	}

	public int getGridY() {
		return gridY;
	}

	public int getPrevGridX() {
		return prevGridX;
	}

	public int getPrevGridY() {
		return prevGridY;
	}
	
	public void restoreGridPosition() {
		gridX = prevGridX;
		gridY = prevGridY;
	}
	
	public void align2Grig() {
		setPosition(gridX*Engine.TILESIZE, gridY*Engine.TILESIZE);
		walking = false;
	}

	public boolean isWalking() {
		return walking;
	}

	public void setStopWithMapColision(boolean checkMapCollision) {
		this.stopWithMapColision = checkMapCollision;
	}

	public boolean isStopWithMapColision() {
		return stopWithMapColision;
	}

}
